package org.xx.armory.web.tags;

import org.apache.commons.text.StringEscapeUtils;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

final class JspRenderer
        implements Closeable, AutoCloseable {
    private final Deque<String> tagNames;
    private final Deque<Boolean> emptyContents;
    private final Map<String, String> attributes;
    private final JspWriter writer;

    JspRenderer(JspWriter writer) {
        Objects.requireNonNull(writer, "writer cannot be null");
        this.tagNames = new ArrayDeque<>();
        this.emptyContents = new ArrayDeque<>();
        this.attributes = new LinkedHashMap<>();
        this.writer = writer;
    }

    private boolean isEmptyContent() {
        return !this.emptyContents.isEmpty() && this.emptyContents.peek();
    }

    private void setEmptyContent(boolean empty) {
        if (this.emptyContents.size() != this.tagNames.size()) {
            throw new IllegalStateException("mismatched empty-contents(" + this.emptyContents.size() + ") and tag-names(" + this.tagNames.size() + ")");
        } else {
            if (!this.emptyContents.isEmpty()) {
                this.emptyContents.pop();
                this.emptyContents.push(empty);
            }

            assert this.emptyContents.size() == this.tagNames.size();

        }
    }

    final void setAttribute(
            String attrName,
            String attrValue
    ) {
        Objects.requireNonNull(attrName, "attrName cannot be null");
        Objects.requireNonNull(attrValue, "attrValue cannot be null");
        this.attributes.put(attrName, attrValue);
    }

    final JspWriter getWriter() {
        return this.writer;
    }

    final void startTag(String tagName)
            throws JspException {
        this.startTag(tagName, true);
    }

    void startTag(
            String tagName,
            boolean emptyContent
    )
            throws JspException {
        this.setEmptyContent(false);

        try {
            this.writer.write("<" + tagName);
            for (final Map.Entry<String, String> entry : this.attributes.entrySet()) {
                this.writer.write(" ");
                this.writer.write(entry.getKey());
                this.writer.write("=");
                this.writer.write("\"" + StringEscapeUtils.escapeHtml4(entry.getValue()) + "\"");
            }

            if (!emptyContent) {
                this.writer.write(">");
            }
        } catch (IOException var5) {
            throw new JspException(var5);
        }

        this.tagNames.push(tagName);
        this.emptyContents.push(emptyContent);
        this.attributes.clear();
    }

    final void comment(String s)
            throws JspException {
        try {
            if (this.isEmptyContent()) {
                this.writer.write(">");
            }

            if (s != null) {
                this.writer.write("<!-- " + s.trim() + " -->");
            }
        } catch (IOException var3) {
            throw new JspException(var3);
        }

        this.setEmptyContent(false);
    }

    final void newline()
            throws JspException {
        try {
            if (this.isEmptyContent()) {
                this.writer.write(">");
            }

            this.writer.println();
        } catch (IOException var2) {
            throw new JspException(var2);
        }

        this.setEmptyContent(false);
    }

    final void write(String s)
            throws JspException {
        try {
            if (this.isEmptyContent()) {
                this.writer.write(">");
            }

            this.writer.write(StringEscapeUtils.escapeHtml4(s));
        } catch (IOException var3) {
            throw new JspException(var3);
        }

        this.setEmptyContent(false);
    }

    final void writeEmptyContent()
            throws JspException {
        try {
            if (this.isEmptyContent()) {
                this.writer.write(">");
            }
        } catch (IOException var2) {
            throw new JspException(var2);
        }

        this.setEmptyContent(false);
    }

    final void writeScript(String s)
            throws JspException {
        try {
            if (this.isEmptyContent()) {
                this.writer.write(">");
            }

            this.writer.write(StringEscapeUtils.escapeEcmaScript(s == null ? "" : s));
        } catch (IOException var3) {
            throw new JspException(var3);
        }

        this.setEmptyContent(false);
    }

    final void writeRaw(Object o)
            throws JspException {
        if (o != null) {
            this.writeRaw(o.toString());
        }

    }

    final void writeRaw(String s)
            throws JspException {
        try {
            if (this.isEmptyContent()) {
                this.writer.write(">");
            }

            this.writer.write(s == null ? "" : s);
        } catch (IOException var3) {
            throw new JspException(var3);
        }

        this.setEmptyContent(false);
    }

    final void endTag()
            throws JspException {
        final boolean emptyContent = this.emptyContents.pop();
        final String tagName = this.tagNames.pop();

        try {
            if (emptyContent) {
                this.writer.write("/>");
            } else {
                this.writer.write("</" + tagName + ">");
            }

            this.writer.flush();
        } catch (IOException var4) {
            throw new JspException(var4);
        }
    }

    public final void close()
            throws IOException {
        this.writer.flush();
    }
}
